#include "ApproximateEntropy.h"

//Test 12

ApproximateEntropy::ApproximateEntropy(bool* bitArray, int bitArraySize) : NistTest(bitArray, bitArraySize)
{
	this->testName = "Approximate Entropy Test";
}


ApproximateEntropy::~ApproximateEntropy()
{
}

void ApproximateEntropy::start() {

	//first Block size
	int m = 0;
	bool *newBitArray;

	//first m entries are copied to the end of the array. For the first test, the last bit is simply ignored
	if (m > 1) {
		newBitArray = new bool[n + m];
		memcpy(newBitArray, bitArray, n);

		for (int i = 0; i < m - 1; i++) {
			newBitArray[n + i] = bitArray[i];
		}
		bitArraySize = n + m - 1;
	}
	else newBitArray = bitArray;

	//Overlapping Count fpr m-sized blocks
	int possibilities = pow(2.0, m);
	int *freqsM1 = new int[possibilities]; //C^i_m
	possibilities = pow(2.0, m+1);
	int *freqsM2 = new int[possibilities]; //C^i_m+1
	int tmpHits = 0;
	int mTest = m;

		for (int i = 0; i < n - mTest; i++) { //run trough the bitArray
			for (int v = 0; v < pow(2, mTest); v++) { //run trough all possible values v for mTest bits
				for (int j = 0; j < mTest; j++) {//Compare all the bits
					if (bitCompare(newBitArray[i + j], j, v)) tmpHits++;
				}
				freqsM1[v] = tmpHits/n;
			}
			tmpHits = 0;
		}
	
		//Compute Psi^m

		double psiM = 0;
		for (int i = 0; i < pow(2, m); i++) {
			psiM += freqsM1[i] * log(freqsM1[i]); //TODO: stimmt das so?! ->log2i checken. Ergebnis stimmt aber.
		}

	//Overlapping Count fpr (m+1)-sized blocks
	mTest = m+1;
	for (int i = 0; i < n - mTest; i++) { //run trough the bitArray
		for (int v = 0; v < pow(2, mTest); v++) { //run trough all possible values v for mTest bits
			for (int j = 0; j < mTest; j++) {//Compare all the bits
				if (bitCompare(newBitArray[i + j], j, v)) tmpHits++;
			}
			freqsM2[v] = tmpHits / n;
		}
		tmpHits = 0;
	}

	//Compute Psi^m+1
	double psiM1 = 0;
	for (int i = 0; i < pow(2, m+1); i++) {
		psiM1 += freqsM2[i] * log(freqsM2[i]); //TODO: stimmt das so?! ->log2i checken. Ergebnis stimmt aber.
	}


	//(6)Compute Test statistics
	double chiSquared = 2 * n*(log(2) - psiM + psiM1);

	//PValue results
	addResult(boost::math::gamma_q(pow(2, m - 1), chiSquared/2));
}

bool ApproximateEntropy::bitCompare(bool bit, int bitPos, int intValue) {
	int mask = 1;
	intValue >>= bitPos;
	return ((int)bit == (intValue & mask));
}

double ApproximateEntropy::log2(int n) {
	return log(n) / log(2);
}